<script setup>
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
import { debounce, get, isEmpty, size, uniq } from 'lodash'
import { useI18n } from 'vue-i18n'
import { useThemeVars } from 'naive-ui'
import useBrowserStore from 'stores/browser.js'
import { EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
import dayjs from 'dayjs'
import Publish from '@/components/icons/Publish.vue'
import Subscribe from '@/components/icons/Subscribe.vue'
import Pause from '@/components/icons/Pause.vue'
import Delete from '@/components/icons/Delete.vue'
import { Publish as PublishSend, StartSubscribe, StopSubscribe } from 'wailsjs/go/services/pubsubService.js'
import Checked from '@/components/icons/Checked.vue'
import Bottom from '@/components/icons/Bottom.vue'
import IconButton from '@/components/common/IconButton.vue'

const themeVars = useThemeVars()

const browserStore = useBrowserStore()
const i18n = useI18n()
const props = defineProps({
  server: {
    type: String,
  },
})

const data = reactive({
  subscribeEvent: '',
  list: [],
  keyword: '',
  autoShowLast: true,
  ellipsisMessage: false,
  channelHistory: [],
})

const publishData = reactive({
  channel: '',
  message: '',
  received: 0,
  lastShowReceived: -1,
})

const tableRef = ref(null)

const columns = computed(() => [
  {
    title: () => i18n.t('pubsub.time'),
    key: 'timestamp',
    width: 180,
    align: 'center',
    titleAlign: 'center',
    render: ({ timestamp }, index) => {
      return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
    },
  },
  {
    title: () => i18n.t('pubsub.channel'),
    key: 'channel',
    filterOptionValue: data.client,
    resizable: true,
    filter: (value, row) => {
      return value === '' || row.client === value.toString() || row.addr === value.toString()
    },
    width: 200,
    align: 'center',
    titleAlign: 'center',
    ellipsis: {
      tooltip: {
        style: {
          maxWidth: '50vw',
          maxHeight: '50vh',
        },
        scrollable: true,
      },
    },
  },
  {
    title: () => i18n.t('pubsub.message'),
    key: 'message',
    titleAlign: 'center',
    filterOptionValue: data.keyword,
    resizable: true,
    className: 'content-value',
    ellipsis: data.ellipsisMessage
      ? {
          tooltip: {
            style: {
              maxWidth: '50vw',
              maxHeight: '50vh',
            },
            scrollable: true,
          },
        }
      : undefined,
    filter: (value, row) => {
      return value === '' || !!~row.cmd.indexOf(value.toString())
    },
  },
])

onMounted(() => {
  // try to stop prev subscribe first
  onStopSubscribe()
})

onUnmounted(() => {
  onStopSubscribe()
})

const isSubscribing = computed(() => {
  return !isEmpty(data.subscribeEvent)
})

const publishEnable = computed(() => {
  return !isEmpty(publishData.channel)
})

const _scrollToBottom = () => {
  nextTick(() => {
    tableRef.value?.scrollTo({ position: 'bottom' })
  })
}
const scrollToBottom = debounce(_scrollToBottom, 300, { leading: true, trailing: true })

const onStartSubscribe = async () => {
  if (isSubscribing.value) {
    return
  }

  const { data: ret, success, msg } = await StartSubscribe(props.server)
  if (!success) {
    $message.error(msg)
    return
  }
  data.subscribeEvent = get(ret, 'eventName')
  EventsOn(data.subscribeEvent, (content) => {
    if (content instanceof Array) {
      data.list.push(...content)
    } else {
      data.list.push(content)
    }
    if (data.autoShowLast) {
      scrollToBottom()
    }
  })
}
const onStopSubscribe = async () => {
  const { success, msg } = await StopSubscribe(props.server)
  if (!success) {
    $message.error(msg)
    return
  }

  EventsOff(data.subscribeEvent)
  data.subscribeEvent = ''
}

const onCleanLog = () => {
  data.list = []
}

const onPublish = async () => {
  if (isEmpty(publishData.channel)) {
    return
  }

  const {
    success,
    msg,
    data: { received = 0 },
  } = await PublishSend(props.server, publishData.channel, publishData.message || '')
  if (!success) {
    publishData.received = 0
    if (!isEmpty(msg)) {
      $message.error(msg)
    }
    return
  }
  publishData.message = ''
  publishData.received = received
  publishData.lastShowReceived = Date.now()
  // save channel history
  data.channelHistory = uniq(data.channelHistory.concat(publishData.channel))

  // hide send status after 2 seconds
  setTimeout(() => {
    if (publishData.lastShowReceived > 0 && Date.now() - publishData.lastShowReceived > 2000) {
      publishData.lastShowReceived = -1
    }
  }, 2100)
}
</script>

<template>
  <div class="content-log content-container fill-height flex-box-v">
    <n-form class="flex-item" label-align="left" label-placement="left" label-width="auto" size="small">
      <n-form-item :show-label="false">
        <n-space :wrap="false" :wrap-item="false" style="width: 100%">
          <n-button v-if="!isSubscribing" :focusable="false" secondary strong type="success" @click="onStartSubscribe">
            <template #icon>
              <n-icon :component="Subscribe" size="18" />
            </template>
            {{ $t('pubsub.subscribe') }}
          </n-button>
          <n-button v-else :focusable="false" secondary strong type="warning" @click="onStopSubscribe">
            <template #icon>
              <n-icon :component="Pause" size="18" />
            </template>
            {{ $t('pubsub.unsubscribe') }}
          </n-button>
          <icon-button
            :icon="Bottom"
            :secondary="data.autoShowLast"
            :type="data.autoShowLast ? 'primary' : 'default'"
            border
            size="18"
            stroke-width="3.5"
            t-tooltip="monitor.always_show_last"
            @click="data.autoShowLast = !data.autoShowLast" />
          <div class="flex-item-expand" />
          <icon-button
            :icon="Delete"
            border
            size="18"
            stroke-width="3.5"
            t-tooltip="pubsub.clear"
            @click="onCleanLog" />
        </n-space>
      </n-form-item>
    </n-form>
    <n-data-table
      ref="tableRef"
      :columns="columns"
      :data="data.list"
      :loading="data.loading"
      class="flex-item-expand"
      flex-height
      size="small"
      virtual-scroll />
    <div class="total-message">{{ $t('pubsub.receive_message', { total: size(data.list) }) }}</div>
    <div class="flex-box-h publish-input">
      <n-input-group>
        <n-auto-complete
          v-model:value="publishData.channel"
          :get-show="() => true"
          :options="data.channelHistory"
          :placeholder="$t('pubsub.channel')"
          style="width: 35%; max-width: 200px"
          @keydown.enter="onPublish" />
        <n-input v-model:value="publishData.message" :placeholder="$t('pubsub.message')" @keydown.enter="onPublish">
          <template #suffix>
            <transition mode="out-in" name="fade">
              <n-tag v-show="publishData.lastShowReceived > 0" bordered size="small" type="success">
                <template #icon>
                  <n-icon :component="Checked" size="16" />
                </template>
                {{ publishData.received }}
              </n-tag>
            </transition>
          </template>
        </n-input>
      </n-input-group>
      <n-button :disabled="!publishEnable" type="info" @click="onPublish">
        <template #icon>
          <n-icon :component="Publish" size="18" />
        </template>
        {{ $t('pubsub.publish') }}
      </n-button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
@import '@/styles/content';

.total-message {
  margin: 10px 0 0;
}

.publish-input {
  margin: 10px 0 0;
  gap: 10px;
}
</style>
